package com.lagou.ipersistence.session.defaults;

import com.lagou.ipersistence.binding.BindingException;
import com.lagou.ipersistence.exceptions.PersistenceException;
import com.lagou.ipersistence.exceptions.ToManyResultException;
import com.lagou.ipersistence.executor.Executor;
import com.lagou.ipersistence.executor.ExecutorException;
import com.lagou.ipersistence.executor.SimpleExecutor;
import com.lagou.ipersistence.mapping.MappedStatement;
import com.lagou.ipersistence.session.Configuration;
import com.lagou.ipersistence.session.RowBounds;
import com.lagou.ipersistence.session.SqlSession;

import java.beans.IntrospectionException;
import java.lang.reflect.*;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

/**
 * @author Yan Jiahong
 * Created 2020/11/8
 */
public class DefaultSqlSession implements SqlSession {
    private final Configuration configuration;
    private final Executor executor;
    private final boolean autoCommit;

    public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.autoCommit = autoCommit;
    }

    @Override
    public <E> E selectOne(String statement) {
        return selectOne(statement, null);
    }

    @Override
    public <E> E selectOne(String statement, Object parameter) {
        List<E> list = this.selectList(statement, parameter);
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.size() > 1) {
            throw new ToManyResultException("通过selectOne()期望一个结果(或者为空), 但是找到了" + list.size() + "个");
        }
        return null;
    }

    @Override
    public <E> List<E> selectList(String statement) {
        return selectList(statement, null);
    }

    @Override
    public <E> List<E> selectList(String statement, Object parameter) {
        return selectList(statement, parameter, RowBounds.DEFAULT);
    }

    @Override
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            MappedStatement ms = configuration.getMappedStatement(statement);
            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
            throw new PersistenceException("从数据库查询时出错, 原因: " + e, e);
        }
    }

    @Override
    public int insert(String statement) {
        return insert(statement, null);
    }

    @Override
    public int insert(String statement, Object parameter) {
        return update(statement, parameter);
    }

    @Override
    public int update(String statement) {
        return update(statement, null);
    }


    @Override
    public int update(String statement, Object parameter) {
        try {
            MappedStatement ms = configuration.getMappedStatement(statement);
            return executor.update(ms, wrapCollection(parameter));
        } catch (Exception e) {
            throw new PersistenceException("从数据库查询时出错, 原因: " + e, e);
        }
    }

    @Override
    public int delete(String statement) {
        return delete(statement, null);
    }

    @Override
    public int delete(String statement, Object parameter) {
        return update(statement, parameter);
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }

    @Override
    public Configuration getConfiguration() {
        return configuration;
    }

    @Override
    public void commit() {
        try {
            executor.commit();
        } catch (Exception e) {
            throw new ExecutorException("提交事务时候出错!");
        }
    }

    @Override
    public void close() {
        try {
            executor.close();
        } catch (Exception e) {
            throw new ExecutorException("关闭连接时候出错!");
        }
    }

    private Object wrapCollection(final Object object) {
        if (object instanceof Collection) {
            StrictMap<Object> map = new StrictMap<>();
            map.put("collection", object);
            if (object instanceof List) {
                map.put("list", object);
            }
            return map;
        } else if (object != null && object.getClass().isArray()) {
            StrictMap<Object> map = new StrictMap<>();
            map.put("array", object);
            return map;
        }
        return object;
    }

    public static class StrictMap<V> extends HashMap<String, V> {
        @Override
        public V get(Object key) {
            if (!super.containsKey(key)) {
                throw new BindingException("参数'" + key + "'没有找到, 可用的参数是: " + this.keySet());
            }
            return super.get(key);
        }
    }
}
